Racket 变量
define 全局绑定
(define x 1)
let 局部绑定
let 可以创建一个作用域,绑定多个变量,同时需要传入一个 body,在 body 中使用局部绑定。
(let ([a 3] [b 2])
(+ a b))
let*
绑定间需要相互引用的时候,需要用 let*:
(let* ([x 10]
[y (* x x)])
(list x y))
let*-values
一个表达式返回多个值,给这些值都进行绑定:
(let*-values ([(x y) (quotient/remainder 10 3)])
(list y x))
letrec 递归绑定
letrec 等效于如下形式:
(letrec ([id expr] ...) body ...+)
letrec 使得它的绑定对所有其它 expr 甚至早期的 expr 都可用。换句话说,letrec 的绑定是递归的。
letrec 中的 exprs 最常见的是递归和相互递归函数的lambda形式。
示例:
> (letrec ([swing
(lambda (t)
(if (eq? (car t) 'tarzan)
(cons 'vine
(cons 'tarzan (cddr t)))
(cons (car t)
(swing (cdr t)))))])
(swing '(vine tarzan vine vine)))
'(vine vine tarzan vine)
其中:
- 通过 letrec 定义了一个局部函数 swing
- 在 body 中调用了该函数
- 在 swing 实现中:
- 如果列表第一个符号是 'tarzan
- 返回一个新列表
- 第一个符号是 'vine
- 第二个符号是 'tarzan
- 后面的是列表 cddr 获取后续元素
- 返回一个新列表
- 否则
- 返回一个新列表
- 第一个元素是 t 的第一个元素
- 其余元素再带入 swing 中进行递归处理
- 返回一个新列表
- 如果列表第一个符号是 'tarzan
Named let
named let 是是一种迭代和递归形式。它使用与局部绑定先沟通的语法关键字 let,但是 let 后面的是标识符(而不是开括号),会触发不同的解析过程。语法定义如下:
(let proc-id ([arg-id init-expr] ...)
body ...+)
named let 等效于:
(letrec ([proc-id (lambda (arg-id ...)
body ...+)])
(proc-id init-expr ...))
也就是说,named let 绑定了一个只在函数主体中可见的函数标识符,它隐含地用一些初始表达式的值来调用该函数。 示例一:
(define (duplicate pos lst)
(let dup ([i 0]
[lst lst])
(cond
[(= i pos) (cons (car lst) lst)]
[else (cons (car lst) (dup (+ i 1) (cdr lst)))])))
> (duplicate 1 (list "apple" "cheese burger!" "banana"))
'("apple" "cheese burger!" "cheese burger!" "banana")
其中:
- 用 named let 创建了 dup
- 第一个 arg-id 是 i,init-expr 是 0
- 第二个 arg-id 是 lst,init-expr 是 lst
- 接下来的 cond 实际上就是以 i = 0、lst = lst 开始执行了
- 如果 i == pos,把当前元素复制一个
- 如果不等于,构建一个新列表,首元素 + 对其余元素递归调用 dup
示例二:
#lang racket
(define/contract (are-numbers-ascending s)
(-> string? boolean?)
(let* ([tokens (string-split s " ")]
[numbers (filter (lambda (token)
(integer? (string->number token)))
tokens)])
(if (empty? numbers)
#t
(let loop ([numbers numbers] [prev-num (string->number (first numbers))])
(if (empty? (rest numbers))
#t
(let ([num (string->number (first (rest numbers)))])
(if (< prev-num num)
(loop (rest numbers) num)
#f)))))))
其中:
- 用 named let 创建了 loop
- numbers = numbers、prev-number 是 numbers 中的第一个数
- 如果遍历到头了(
if (empty? (rest numbers))
),说明满足递增,返回 true - 否则,取出下一个数命名为 num,将下一个数与当前数判断大小,如果不符合规则,直接给出结果,否则进行递归